#ifdef _WIN32

#include "thread.h"
#include <windows.h>

using namespace krryn::core;
using namespace krryn::core_imp;

namespace krryn{
namespace core_imp{
	class blockable{
	public:
		virtual HANDLE handle() = 0;
	};

	class event_handle : public blockable{
	public:
		HANDLE m_Event;

		virtual HANDLE handle(){return m_Event;}
	};

	class thread_handle : public blockable{
	public:
		HANDLE m_Thread;

		thread_handle() : m_Thread(0) {
			
		}

		static DWORD WINAPI callback(LPVOID a_Param){
			if(thread* l_Param = reinterpret_cast<thread*>(a_Param))
				l_Param->run();
			return 0;
		}

		virtual HANDLE handle(){return m_Thread;}
	};

	class mutex_handle{
	public:
		LPCRITICAL_SECTION m_CriticalSection;

		mutex_handle(){
			m_CriticalSection = new CRITICAL_SECTION;
		}

		~mutex_handle(){
			if(m_CriticalSection)
				delete m_CriticalSection, m_CriticalSection = 0;
		}
	};
}
}

event::event(const std::string &a_Name) : m_Name(a_Name){
	m_Handle = new event_handle();
	m_Handle->m_Event = CreateEvent(NULL, FALSE, FALSE, NULL);
}

event::~event(){
	delete m_Handle, m_Handle = 0;
}

mutex::mutex(){
	m_Handle = new mutex_handle();
	InitializeCriticalSection(m_Handle->m_CriticalSection);
}

mutex::~mutex(){
	if(m_Handle){
		DeleteCriticalSection(m_Handle->m_CriticalSection);
		delete m_Handle, m_Handle = 0;
	}
}

void mutex::lock(){
	EnterCriticalSection(m_Handle->m_CriticalSection);
}

void mutex::unlock(){
	LeaveCriticalSection(m_Handle->m_CriticalSection);
}

std::string event::name(){
	return m_Name;
}

void event::trigger() volatile{
	SetEvent(m_Handle->m_Event);
}

bool event::wait() volatile{
	WaitForSingleObject(m_Handle->m_Event, INFINITE);
	return true;
}

thread::thread(){
	m_Handle = new thread_handle();
}

thread::~thread(){
	kill();

	delete m_Handle, m_Handle = 0;
}

void thread::start(){
	DWORD l_Temp;

	m_Handle->m_Thread = CreateThread(
		NULL, 0, 
		thread_handle::callback, 
		(LPVOID)this, NULL,
		&l_Temp
	);
}

void thread::end(){
	if(m_Handle == 0 || m_Handle->m_Thread == 0)
		return;
	WaitForSingleObject(m_Handle->m_Thread, INFINITE);
	CloseHandle(m_Handle->m_Thread);
	m_Handle->m_Thread = 0;
}

void thread::sleep(long a_MilliSeconds){
	Sleep(a_MilliSeconds);
}

void thread::kill(){
	TerminateThread(m_Handle->m_Thread, 0);
}

wait_all::wait_all(thread **a_Thread, int a_Amount){
	HANDLE *l_Handles = new HANDLE[a_Amount];

	for(int i = 0; i < a_Amount; i++){
		if(a_Thread[i]->m_Handle != 0)
			l_Handles[i] = a_Thread[i]->m_Handle->handle();
	}

	WaitForMultipleObjects(a_Amount, l_Handles, TRUE, WAIT_TIMEOUT);

	for(int i = 0; i < a_Amount; i++){
		a_Thread[i]->end();
	}
}

#endif
